#pragma once

#include "znx_conn.h"
#include "znx_worker_proc.h"
#include "comm/znx_rbtree.h"
#include "comm/znx_inet.h"


typedef struct znx_session_pool_s   znx_session_pool_t;


typedef struct {
    znx_rbtree_node_t           tree_node;
    znx_queue_t                 lru_queue;
    znx_conn_t                  *conn;
    znx_str_t                   key;
    uint32_t                    key_crc;
    znx_session_pool_t          *pool;
    znx_conn_free_handle_pt     free_handle;
} znx_session_item_t;


static inline int
znx_session_item_cmp(znx_rbtree_node_t *a, znx_rbtree_node_t *b)
{
    znx_session_item_t  *item_a = (znx_session_item_t *)a;
    znx_session_item_t  *item_b = (znx_session_item_t *)b;

    if (item_a->key_crc > item_b->key_crc) {
        return 1;
    }

    if (item_a->key_crc < item_b->key_crc) {
        return -1;
    }

    if (item_a->key.len > item_b->key.len) {
        return 1;
    }

    if (item_a->key.len < item_b->key.len) {
        return -1;
    }

    return znx_strncmp(item_a->key.data,
        item_b->key.data, item_a->key.len);
}


struct znx_session_pool_s {
    size_t                      max_conns;
    size_t                      cur_conns;
    znx_queue_t                 lru_head;
    znx_rbtree_t                tree;
    znx_rbtree_node_t           tree_sentinel;
};


static inline void
znx_session_pool_default_free_conn(znx_conn_t *conn)
{
    znx_event_loop_remove_conn(conn);
    znx_conn_free(conn);
}


// modified only during the initialization phase
extern size_t g_session_pool_max_conns;

extern __thread znx_session_pool_t *t_session_pool;


znx_session_pool_t *znx_session_pool_create(size_t max_conns);

void znx_session_pool_put(znx_session_pool_t *pool,
    znx_str_t *tag, znx_conn_t *conn, int64_t timeout_ms,
    znx_conn_free_handle_pt free_handle);

// the address must be reinitialized after bero.
znx_conn_t *znx_session_pool_get(znx_session_pool_t *pool,
    znx_str_t *tag, znx_address_t *address);

void znx_session_pool_clean(znx_session_pool_t *pool);

void znx_session_pool_destroy(znx_session_pool_t *pool);

znx_bool_t znx_session_conn_avalive_check(znx_conn_t *conn);


void znx_t_session_pool_init(size_t max_conns);


static inline void
znx_t_session_pool_put(znx_str_t *tag,
    znx_conn_t *conn, int64_t timeout_ms,
    znx_conn_free_handle_pt free_handle)
{
    if (free_handle == NULL) {
        free_handle = znx_session_pool_default_free_conn;
    }

    if (t_session_pool == NULL) {
        free_handle(conn);
        return;
    }

    znx_session_pool_put(t_session_pool, tag,
        conn, timeout_ms,free_handle);
}


static inline znx_conn_t *
znx_t_session_pool_get(znx_str_t *tag, znx_address_t *address)
{
    if (t_session_pool == NULL) {
        return NULL;
    }

    return znx_session_pool_get(t_session_pool, tag, address);
}

